from django.shortcuts import render,redirect
import re
from django.urls import reverse
from django.views import View
from django.contrib.auth import login, authenticate, logout, mixins
from django.http import JsonResponse, HttpResponseForbidden
from django_redis import get_redis_connection
from django.conf import settings
import json
from random import randint

from .models import *
from meiduo.utils.response_code import *
from users.utils import analysis_url, get_url
from celery_tasks.emails.tasks import send_email_client
from celery_tasks.sms.tasks import ccp_send_message_code
from meiduo.utils.judgelogin import UserLoginView
from goods.models import *
from users import const
from carts.utils import shopping_car_combine
from meiduo.utils.encrypt import *
from verification.constants import *

import logging

logger = logging.getLogger("django")


class Register(View):
    def get(self, request):

        return render(request,"register.html")

    def post(self,request):
        """用户注册POST请求响应视图"""

        response = request.POST
        # 获取FROM表单中数据
        username = response.get("username","")
        password = response.get("password","")
        password2 = response.get("password2","")
        phone_number = response.get("phone_number","")
        sms_code = response.get("sms_code","")
        allow = response.get("allow",'')

        if not all([username,password,phone_number,sms_code,allow]):
            return render(request,"404.html")
        if not re.match("^[a-zA-Z0-9_-]{5,20}$",username):
            return render(request,"404.html")
        if not re.match("^[0-9A-Za-z]{8,20}$",password) or not (password == password2):
            return render(request,"404.html")
        if not re.match("^1[345789]\d{9}$",phone_number):
            return render(request,"404.html")
        if allow != "on":
            return render(request,"404.html")

        conn = get_redis_connection(alias="verification")
        # 连接redis 2#库
        sms_code_send = conn.get(phone_number)
        # 获取手机号码对应的验证码
        if sms_code_send is None:
            return render(request,"register.html",{'register_errmsg':"短信验证码过期"})
        sms_code_send = sms_code_send.decode()
        if sms_code != sms_code_send:
            return render(request,"register.html",{'register_errmsg':"短信验证码错误"})
        user = User.objects.create_user(username=username, password=password, phone_number=phone_number)
        # 数据库中存入user数据

        login(request,user)
        # 保持user登录状态   把user的id存入session中，再将session_id存入cookie中返还给浏览器

        response = redirect("/")
        response.set_cookie("username",value=username,max_age=settings.SESSION_COOKIE_AGE)
        # 设置会话cookie  过期时长与session同步

        return response


class CheckUserName(View):
    """检查是否用户名是否重复"""
    def get(self,request,username):
        count = User.objects.filter(username=username).count()
        return JsonResponse({"count": count})


class CheckMobile(View):
    """检查手机号是否重复"""
    def get(self,request,mobile):
        count = User.objects.filter(phone_number=mobile).count()
        return JsonResponse({"count": count})


class Login(View):
    """响应注册页面"""
    def get(self,request):
        return render(request,"login.html")

    def post(self,request):

        info_dirt = request.POST
        username = info_dirt.get("username")
        password = info_dirt.get("password")
        remember_allow = info_dirt.get("remembered")

        """  通过修改类属性达到多账号登陆  此方法弊端：  类属性通用，多线程会出现资源争抢问题
        if re.match("^1[345789]\d{9}$",username):
            User.USERNAME_FIELD = "phone_number"
            user = authenticate(username=username,password=password)
        else:
            User.USERNAME_FIELD = "username"
            user = authenticate(username=username, password=password)
        # authenticate 匹配数据库中是否有此用户，有返回此user对象，无返回None
        """

        """在util中重新定义该方法   前面先判断是否为手机号或用户名"""
        user = authenticate(username=username,password=password)

        if user is None:
            return render(request,"login.html",{"account_errmsg": "用户名密码错误！"})

        login(request,user)

        path_dict = request.GET
        come_path = path_dict.get("next")
        go_path = come_path or "/"
        response = redirect(go_path)
        # 根据来源路径返回相应路径页面

        if remember_allow != "on":
            request.session.set_expiry(0)
            # 设置session 关闭浏览器是清除
            response.set_cookie("username",value=username,max_age=None)
        else:
            response.set_cookie("username",value=username,max_age=settings.SESSION_COOKIE_AGE)
            # 设置会话cookie  过期时长与session同步

        shopping_car_combine(request,response)   # 合并购物车
        return response


class Logout(View):
    """定义用户退出视图"""
    def get(self,request):
        logout(request)
        # 推出用户状态   删除sessionid
        response = redirect("/login/")
        response.delete_cookie("username")
        # 删除cookie

        return response


class UserInfo(mixins.LoginRequiredMixin,View):
    """个人中心视图"""
    def get(self,request):

        return render(request,"user_center_info.html")
        # if request.user.is_authenticated:
        #     return redirect("/login/?next=info")
        #
        # return render(request,"user_center_info.html")


class EmailDeal(UserLoginView):
    """email处理"""
    def put(self,request):
        email_dict = json.loads(request.body.decode())
        email = email_dict.get("email")
        if email is None:
            return render(request,"404.html")
        if not re.match("[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$",email):
            return render(request,"404.html")

        user = request.user
        username = user.username
        verify_url = get_url(email, username)
        # 将user_id  username 加密放入url查询字符串中
        send_email_client.delay(verify_url,email)
        # 发送邮件

        return JsonResponse({"code":RETCODE.OK,"errmsg":"OK"})


class EmailRegister(View):
    """邮件注册"""
    def get(self,request):
        verification_data = request.GET.get("token")
        # 接受访问路径中的查询字符串
        if verification_data is None:
            return render(request,"404.html")
        email, username = analysis_url(verification_data)
        if all([username,email]) is False:
            return render(request,"404.html")
        try:
            # 判断传回参数是否正确
            user = User.objects.get(username=username)
            user.email = email
            user.email_active = True
            user.save()
            return redirect((reverse("info")))
        except User.DoesNotExist:
            return render(request,"404.html")


class AddressesShow(UserLoginView):
    """获取城市地址视图"""
    def get(self,request):
        # 查询出所有收货地址  通过jijia2模板渲染

        # 查出所有收货地址
        user = request.user
        addresses = Addresses.objects.filter(user=user,is_deleted=False)
        if not addresses:
            return render(request, "user_center_site.html")

        # 将收货地址对象转化为i通用的列表格式
        addresses_list = []
        for address in addresses:
            addresses_list.append({
                "id":address.id,
                "title":address.title,
                "receiver":address.receiver,
                "province_id":address.province_id,
                "province":address.province.area_name,
                "city_id":address.city_id,
                "city":address.city.area_name,
                "district_id":address.district_id,
                "district":address.district.area_name,
                "place":address.place,
                "mobile":address.mobile,
                "tel":address.tel,
                "email":address.email,
            })
        context = {
            "addresses":addresses_list,
            "default_address_id":user.address_default_id
        }

        # 模板渲染
        return render(request,"user_center_site.html",context=context)


class CreateAddress(UserLoginView):
    """用户新增地址视图"""
    def post(self,request):
        # 接收数据
        dict = json.loads(request.body.decode())
        title = dict.get("title")
        receiver = dict.get("title")
        province_id = dict.get("province_id")
        city_id = dict.get("city_id")
        district_id = dict.get("district_id")
        place = dict.get("place")
        mobile = dict.get("mobile")
        tel = dict.get("tel",)
        email = dict.get("email",)

        # 校验数据
        if not all([title,receiver,province_id,city_id,district_id,place,mobile]):
            return render(request,"404.html")
        if not re.match("^1[345789]\d{9}$",mobile):
            return JsonResponse({"code":RETCODE.MOBILEERR,"errmsg":"手机号输入错误"})
        if tel:
            if not re.match("^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$",tel):
                return JsonResponse({"code":RETCODE.TELERR,"errmsg":"电话号输入错误"})
        if email:
            if not re.match(("^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$"),email):
                return JsonResponse({"code":RETCODE.EMAILERR,"errmsg":"邮箱输入错误"})

        # 逻辑处理
        user = request.user
        try:
            count =  Addresses.objects.filter(user=user, is_deleted=False).count()
            if count >= 20:
                # 设置收货地址上限
                return JsonResponse({"code":RETCODE.STOCKERR,"errmsg":"收货地址超上限"})
            address = Addresses.objects.create(
                user=user,
                title=title,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email,
                is_deleted=False
            )
            if count == 0:
                # 设置默认地址
                user.address_default=address
                user.save()
            # 响应
            address_dict = {
                "id":address.id,
                "title":title,
                "receiver":receiver,
                "province_id":province_id,
                "province":address.province.area_name,
                "city_id":city_id,
                "city":address.city.area_name,
                "district_id":district_id,
                "district":address.district.area_name,
                "place":place,
                "monile":mobile,
                "tel":tel,
                "email":email
            }
            return JsonResponse({"code":RETCODE.OK,"errmsg":"ok","address":address_dict})

        except Exception as e:
            logger.error(e)
            return JsonResponse({"code":RETCODE.DBERR,"errmsg":"地址添加失败"})


class ChangeAddress(UserLoginView):
    def put(self,request,id):
        """修改收货地址"""

        address_dict = json.loads(request.body.decode())
        # 接受前端传来数据
        title = address_dict.get("title")
        receiver = address_dict.get("receiver")
        province_id = address_dict.get("province_id")
        city_id = address_dict.get("city_id")
        district_id = address_dict.get("district_id")
        place = address_dict.get("place")
        mobile = address_dict.get("mobile")
        tel = address_dict.get("tel")
        email = address_dict.get("email")

        # 校验数据
        if not all([title,receiver,province_id,city_id,district_id,place,mobile]):
            return render(request,"404.html")
        if not re.match("^1[345789]\d{9}$",mobile):
            return JsonResponse({"code":RETCODE.MOBILEERR,"errmsg":"手机号输入错误"})
        if tel:
            if not re.match("^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$",tel):
                return JsonResponse({"code":RETCODE.TELERR,"errmsg":"电话号输入错误"})
        if email:
            if not re.match(("^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$"),email):
                return JsonResponse({"code":RETCODE.EMAILERR,"errmsg":"邮箱输入错误"})

        # 逻辑业务处理
        try:
            address = Addresses.objects.get(id=id,user=request.user,is_deleted=False)
            # 查找address对象
            address.title = title
            address.receiver = receiver
            address.province_id = province_id
            address.city_id = city_id
            address.district_id = district_id
            address.place = place
            address.mobile = mobile
            address.tel = tel
            address.email = email
            address.save()
            # 修改指定address对象属性

        # 响应
        except Exception as e:

            return JsonResponse({"code":RETCODE.DBERR,"errmsg":"地址添加失败"})
        else:
            address_dict = {
                "id":address.id,
                "title":address.title,
                "receiver":address.receiver,
                "province_id":address.province_id,
                "province":address.province.area_name,
                "city_id":address.city_id,
                "city":address.city.area_name,
                "district_id":address.district_id,
                "district":address.district.area_name,
                "place":address.place,
                "mobile":address.mobile,
                "tel":address.tel,
                "email":address.email,
            }
            return JsonResponse({"code":RETCODE.OK,"errmsg":"ok","address":address_dict})

    def delete(self,request,id):
        # 删除指定收货地址
        user = request.user
        try:
            address = Addresses.objects.get(id=id,user=user,is_deleted=False)
            # 尝试获取需要删除的收货地址对象
            address.is_deleted=True
            # 逻辑删除
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({"code":RETCODE.PWDERR,"errmsg":"删除失败"})
        else:
            return JsonResponse({"code":RETCODE.OK,"errmsg":"ok"})


class SetDefaultAddress(UserLoginView):
    """设置默认地址视图"""
    def put(self,request,id):
        user = request.user
        try:
            address = Addresses.objects.get(id=id,user=user,is_deleted=False)
            # 获取指定收货地址对象  并修改默认地址
            user.address_default = address
            user.save()

        except Exception as e:
            logger.error(e)
            return JsonResponse({"code":RETCODE.PWDERR,"errmsg":"设置失败"})
        else:
            return JsonResponse({"code":RETCODE.OK,"errmsg":"ok"})


class ChangeAddressTitle(UserLoginView):
    """修改指定地址标题"""
    def put(self,request,id):
        title = json.loads(request.body.decode()).get("title")
        # 获取需修改的标题
        user = request.user
        if not title:
            # 校验数据
            return render(request,"404.html")

        try:
            address = Addresses.objects.get(id=id,user=user,is_deleted=False)
            # 查询需要修改的address对象   并修改标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({"code":RETCODE.PWDERR,"errmsg":"修改失败"})
        else:
            return JsonResponse({"code":RETCODE.OK,"errmsg":"ok"})


class ChangePassword(UserLoginView):
    """修改密码视图"""
    def get(self,request):
        # 获取修改密码视图
        return render(request,"user_center_pass.html")

    def post(self,request):
        # 修改密码
        # 接受数据
        pwd_dict = request.POST
        old_pwd = pwd_dict.get("old_pwd")
        new_pwd = pwd_dict.get("new_pwd")
        new_cpwd = pwd_dict.get("new_cpwd")

        # 校验数据
        if not re.match("^[0-9A-Za-z]{8,20}$",old_pwd):
            return render(request,"user_center_pass.html",{"origin_pwd_errmsg":"密码格式错误"})
        if not request.user.check_password(old_pwd):
            return render(request,"user_center_pass.html",{"origin_pwd_errmsg":"密码错误"})
        if not re.match("^[0-9A-Za-z]{8,20}$",new_pwd):
            return render(request,"user_center_pass.html",{"change_pwd_errmsg":"密码格式错误"})
        if new_pwd != new_cpwd:
            return render(request,"user_center_pass.html",{"change_pwd_errmsg":"密码不一致"})

        # 逻辑处理
        try:
            request.user.set_password(new_pwd)
            # 修改当前用户密码
            request.user.save()
            # 重定向至登陆界面
            return redirect("/logout/")
        except Exception as e:
            logger.error(e)
            return render(request,"user_center_pass.html",{"change_pwd_errmsg":"密码修改失败"})
        # 响应


class BrowseHistories(View):
    """浏览记录视图"""
    def post(self, request):
        user = request.user
        if not user.is_authenticated:
            # 判断用户是否登录
            return JsonResponse({"code":RETCODE.SESSIONERR,"errmsg":"用户未登录"})

        #  接受数据
        sku_id = json.loads(request.body.decode()).get("sku_id")

        # 验证数据
        if not sku_id:
            return JsonResponse({"code": RETCODE.PARAMERR, "errmsg": "参数错误"})
        try:
            SKU.objects.get(id=sku_id,is_launched=True)
        except SKU.DoesNotExist:
            return JsonResponse({"code":RETCODE.DBERR,"errmsg":"传入数据错误"})

        # 业务逻辑
        id = user.id
        conn = get_redis_connection(alias="histories")
        pl = conn.pipeline()
        pl.lrem("histories_%d"%id, 0, sku_id)
        # 去重
        pl.lpush("histories_%d"%id, sku_id)
        # 添加
        pl.ltrim("histories_%d" % id, 0, const.histories_show_amount - 1)
        # 保留列表前五位数字
        pl.expire("histories_%d" % id, const.histories_expire_time)
        #  设置浏览记录保存时间
        pl.execute()
        # 响应数据
        return JsonResponse({"code": RETCODE.OK, "errmsg": "ok"})

    def get(self,request):
        # 获取浏览记录 并在用户中心显示
        user = request.user
        if not user.is_authenticated:
            # 验证用户是否登录
            return render(request,"404.html")

        conn = get_redis_connection(alias='histories')
        skus_id_list = conn.lrange("histories_%d"%user.id,0,-1)
        if not skus_id_list:
            return JsonResponse({"code":RETCODE.NODATAERR,"errmsg":"无数据"})
        skus_list = list()

        for sku_id in skus_id_list:
            sku = SKU.objects.get(id=sku_id)
            skus_list.append({
                "id":sku.id,
                "name":sku.name,
                "price":sku.price,
                "default_image_url":sku.default_image.url,
                "url":"/detail/%d/"%sku.id,
            })
        return JsonResponse({"code":RETCODE.OK,"errmsg":"ok","skus":skus_list})


class ForgetPassword(View):
    """忘记密码视图"""
    def get(self,request):

        return render(request,"find_password.html")


class RetrievePassword(View):
    """找回密码视图"""
    def get(self,request,username):
        # 接受数据
        data_dict = request.GET
        text = data_dict.get("text")
        image_code_id = data_dict.get("image_code_id")

        # 校验数据
        conn = get_redis_connection(alias="verification")
        image_code = conn.get(image_code_id).decode()
        if image_code.lower() != text.lower():
            return JsonResponse({},status=400)
        if re.match("^1[345789]\d{9}$",username):
            phone_number = username
            try:
                user = User.objects.get(phone_number=phone_number)
            except User.DoesNotExist:
                return JsonResponse({}, status=404)
        else:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                return JsonResponse({}, status=404)
        token = {
            "username":user.username,
            "password":user.password
        }
        access_token = encrypts(token)
        data = {
            "mobile":user.phone_number,
            "access_token":access_token
        }
        return JsonResponse(data)


class SendSMSCode(View):
    """发送短信验证码"""
    def get(self,request):
        conn = get_redis_connection("verification")
        data_dict = request.GET
        access_token= request.GET.get("access_token")
        try:
            token = decrypts(access_token)
        except:
            return JsonResponse({"message":"请求超时"},status=403)
        username = token["username"]
        password = token["password"]
        try:
            user = User.objects.get(username=token["username"],password=token["password"])
        except User.DoesNotExist:
            return HttpResponseForbidden("access_token错误")

        mobile = user.phone_number
        sms_random_code = "%06d" % randint(1,999999)

        pl = conn.pipeline()
        # 创建redis管道
        # conn.setex(mobile,message_code_expiry,sms_random_code)
        pl.setex(mobile, message_code_expiry, sms_random_code)
        # 存储sms验证码信息
        pl.execute()

        logger.error(sms_random_code)
        ccp_send_message_code.delay(mobile,sms_random_code,message_reminder_expiry,templates_num)

        return JsonResponse({"code":RETCODE.OK,"errmsg":"ok"})


class VerificationSMSCode(View):
    """验证sms_code视图"""
    def get(self,request,mobile):
        sms_code_get = request.GET.get("sms_code")
        if not sms_code_get:
            return HttpResponseForbidden("参数错误")
        try:
            user = User.objects.get(phone_number=mobile)
        except User.DoesNotExist:
            # 手机号不存在
            return JsonResponse({},status=404)

        conn = get_redis_connection("verification")
        sms_code = conn.get(mobile).decode()
        if sms_code_get != sms_code:
            # 验证码错误
            return JsonResponse({},status=400)

        data = {
            "user_id":user.id,
            "access_token":user.password,
        }
        return JsonResponse(data)


class AppendPassword(View):
    def post(self,request,user_id):
        """修改密码视图"""
        data_dict = json.loads(request.body.decode())
        password = data_dict.get("password")
        password2 = data_dict.get("password2")
        access_token = data_dict.get("access_token")

        if not all([password,password2,access_token]):
            return HttpResponseForbidden("缺少参数")
        if len(password) <= 8 and len(password) >= 20:
            return HttpResponseForbidden("密码错误")
        if password != password2:
            return HttpResponseForbidden("密码不一致")
        try:
            user = User.objects.get(id=user_id,password=access_token)
        except User.DoesNotExist:
            return HttpResponseForbidden("access_token错误")

        user.set_password(password)
        user.save()

        return JsonResponse({"code":RETCODE.OK,"errmsg":"ok"})


